#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/clk.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/phy.h>
#include <linux/dma-mapping.h>
#include <linux/spi/eeprom.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/if_alg.h>
#include <linux/mmc/host.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/pmu.h>
#include <mach/io.h>
#include <mach/board_config.h>
#include <mach/pinctrl.h>
#include <mach/clock.h>
#include <mach/fh_uart_plat.h>
#include <mach/fh_mci_plat.h>
#include <mach/fh_mshc_plat.h>
#include <mach/fh_gmac_plat.h>
#include <mach/fh_gpio_plat.h>
#include <mach/mol_efuse_plat.h>
#include <mach/fh_dma_plat.h>
#include <mach/fh_spi_plat.h>
#include <mach/fh_i2s_plat.h>
#include <mach/fh_pwm_plat.h>
#include <mach/fh_wdt_plat.h>
#include <mach/fh_usb_plat.h>
#include <mach/fh_sadc_plat.h>
#include <mach/fh_rtc_plat.h>
#include <mach/fh_cesa_plat.h>
#include <mach/platsmp.h>
#include "chip.h"
#include "../soc.h"
#include <linux/gpio.h>
#include <linux/irqchip/arm-gic.h>




struct save_register_t {
	unsigned int add;
	unsigned int val;
	//if == 0, means set_reg or set_reg_m
	unsigned int mask;

	unsigned int save_val;
	unsigned int lp_val;
};

struct aov_reg_module_t {
	char *name;
	struct save_register_t *reg_map;
	unsigned int map_size;
	int index;
};


#define MAX_REC_AOV_MAP_SIZE	0x20
struct aov_reg_element {
	unsigned int phy_add_base;
	unsigned int va_add_base;
};

struct aov_reg_remap{
	struct aov_reg_element reg_map[MAX_REC_AOV_MAP_SIZE];
	unsigned int index;
};

struct aov_reg_remap g_aov_reg_map_obj = {0};



#define AOV_REG_INFO(_add, _set_val, _set_mask) \
	.add = _add, \
	.val = _set_val,\
	.mask = _set_mask,\
	.save_val = 0,\
	.lp_val = 0,


#define AOV_DECLARE_REG_LIST(name) \
struct save_register_t aov_reg_map_##name[]


#define AOV_DECLARE_GLOBAL_OBJ(name) \
struct aov_reg_module_t aov_reg_module_##name = {#name, aov_reg_map_##name, sizeof(aov_reg_map_##name)/sizeof(struct save_register_t), 0}


static unsigned int aov_got_vadd(unsigned int add){
	unsigned int phy_add_mask;
	unsigned int va_add_mask;
	unsigned int va_add;
	int i;

	phy_add_mask = add & (~(SZ_16K - 1));
	//add -> vadd;
	for(i = 0 ; i < MAX_REC_AOV_MAP_SIZE; i++){
		//find valid map
		if(phy_add_mask == g_aov_reg_map_obj.reg_map[i].phy_add_base){
			//got valid add map
			va_add_mask = g_aov_reg_map_obj.reg_map[i].va_add_base;
			break;
		}
	}

	if(i == MAX_REC_AOV_MAP_SIZE){
		//got no valid add .then try remap...
		if(g_aov_reg_map_obj.index >= MAX_REC_AOV_MAP_SIZE){
			pr_err("ERR :: size[%d] too low...\n",MAX_REC_AOV_MAP_SIZE);
			BUG_ON(1);
		}
		else{
			va_add_mask = ioremap(phy_add_mask, SZ_16K);
			if (!va_add_mask) {
				BUG_ON(1);
			}
			//rec to map...
			g_aov_reg_map_obj.reg_map[g_aov_reg_map_obj.index].phy_add_base = phy_add_mask;
			g_aov_reg_map_obj.reg_map[g_aov_reg_map_obj.index].va_add_base = va_add_mask;
			g_aov_reg_map_obj.index++;
		}
	}
	va_add = va_add_mask + (add & (SZ_16K - 1));
	//pr_err("padd :: va_add  = %x : %x\n",add , va_add);
	return va_add;
}


static  void __AOV_PUSH_REG(struct aov_reg_module_t *p_module, unsigned int add_in, unsigned int val){

	unsigned int ret;
	unsigned int add;


	add = aov_got_vadd(add_in);


	ret = GET_REG(add);
	if(p_module->index >= p_module->map_size){
		pr_err("[%s] :: raw size[%x] need to more mem....\n", p_module->name, p_module->map_size);
		BUG_ON(1);
	}
	p_module->reg_map[p_module->index].add = add_in;
	p_module->reg_map[p_module->index].save_val = ret;
	p_module->reg_map[p_module->index].lp_val = val;
	p_module->index++;
	SET_REG(add, val);

}


static  void __AOV_PUSH_REG_M(struct aov_reg_module_t *p_module, unsigned int add_in, unsigned int val, unsigned int mask){

	unsigned int ret;
	unsigned int add;

	add = aov_got_vadd(add_in);

	ret = GET_REG(add);
	if(p_module->index >= p_module->map_size){
		pr_err("[%s] :: raw size[%x] need to more mem....\n", p_module->name, p_module->map_size);
		BUG_ON(1);
	}
	p_module->reg_map[p_module->index].add = add_in;
	p_module->reg_map[p_module->index].save_val = ret;
	ret = ret & (~(mask));
	ret |= (val & mask);
	p_module->reg_map[p_module->index].lp_val = ret;
	p_module->index++;
	SET_REG(add, ret);

}

static  void  __AOV_PUSH_ALL_REGS(struct aov_reg_module_t *p_module){

	int i;

	for(i = 0; i < p_module->map_size; i++){
		if(p_module->reg_map[i].mask != 0)
			__AOV_PUSH_REG_M(p_module, p_module->reg_map[i].add, p_module->reg_map[i].val, p_module->reg_map[i].mask);
		else
			__AOV_PUSH_REG(p_module, p_module->reg_map[i].add, p_module->reg_map[i].val);
	}

}


static  void __AOV_POP_REG(struct aov_reg_module_t *p_module){

	unsigned int add;
	if(p_module->index == 0){
		pr_err("[%s] :: underflow....\n", p_module->name);
		BUG_ON(1);
	}
	//pr_err("[POP] :: [%08x] = [%08x]\n",p_module->reg_map[p_module->index - 1].add,  p_module->reg_map[p_module->index - 1].save_val);

	add = aov_got_vadd(p_module->reg_map[p_module->index - 1].add);
	SET_REG(add, p_module->reg_map[p_module->index - 1].save_val);
	p_module->index--;

}


static  void __AOV_POP_ALL_REGS(struct aov_reg_module_t *p_module){

	while(p_module->index){
		__AOV_POP_REG(p_module);
	}

}

static  void __AOV_DUMP_MODULE(struct aov_reg_module_t *p_module){

	int i;

	pr_err("%s -------->\n",p_module->name);
	pr_err("MAX used [0x%x] -> [0x%x]\n",p_module->map_size, p_module->index);
	for(i = 0; i < p_module->index; i ++){
		pr_err("\t[%08x] :: SA[%08x] : LP[%08x]\n",
		p_module->reg_map[i].add,
		p_module->reg_map[i].save_val,
		p_module->reg_map[i].lp_val);
	}

}

#define AOV_PUSH_REG(name,addr,val) __AOV_PUSH_REG(&aov_reg_module_##name, addr,val)
#define AOV_PUSH_REG_M(name,addr,val, mask) __AOV_PUSH_REG_M(&aov_reg_module_##name, addr,val, mask)
#define AOV_PUSH_ALL_REGS(name) __AOV_PUSH_ALL_REGS(&aov_reg_module_##name)

#define AOV_POP_REG(name) __AOV_POP_REG(&aov_reg_module_##name)
#define AOV_POP_ALL_REGS(name) __AOV_POP_ALL_REGS(&aov_reg_module_##name)
#define AOV_DEBUG(name) __AOV_DUMP_MODULE(&aov_reg_module_##name)


AOV_DECLARE_REG_LIST(aud) = {
	{AOV_REG_INFO(0x2c500080 + 0x08, 1 << 4 | 1 << 1, 1 << 4 | 1 << 1)},
	{AOV_REG_INFO(0x2c500080 + 0x1c, 1 << 3 | 1 << 1, 1 << 3 | 1 << 1)},
	{AOV_REG_INFO(0x2c500080 + 0x4c, 0, 1 << 2 | 1 << 0)},
	{AOV_REG_INFO(0x2c500080 + 0x5c, 1 << 2 | 1 << 0, 1 << 2 | 1 << 0)},
};
AOV_DECLARE_GLOBAL_OBJ(aud);

/****** usb *******/
AOV_DECLARE_REG_LIST(usb) = {
	{AOV_REG_INFO(0x1ca0003c, 1 << 8, 1 << 8)},
};
AOV_DECLARE_GLOBAL_OBJ(usb);

/****** ephy *******/
AOV_DECLARE_REG_LIST(ephy) = {
	{AOV_REG_INFO(0x1C900000 + 0x0c, 0xba1edfff, 0)},
	{AOV_REG_INFO(0x1C900000 + 0x10, 0x7fcbf9fe, 0)},
	{AOV_REG_INFO(0x1C900000 + 0x00, 0x20400080, 0)},
	//{AOV_REG_INFO(0x1C900000 + 0x28, 0x00287870, 0)},
	{AOV_REG_INFO(0x1C900000 + 0x28, 0x007e7875, 0)},
};
AOV_DECLARE_GLOBAL_OBJ(ephy);

/****** ldo *******/
AOV_DECLARE_REG_LIST(ldo) = {
	{AOV_REG_INFO(0x10000578, 0x4e, 0)},
	{AOV_REG_INFO(0x1000057c, 0x4e, 0)},
	{AOV_REG_INFO(0x10000578, 0x4a, 0)},
	{AOV_REG_INFO(0x1000057c, 0x4a, 0)},
};
AOV_DECLARE_GLOBAL_OBJ(ldo);

/****** sadc *******/
AOV_DECLARE_REG_LIST(sadc) = {
	{AOV_REG_INFO(0x1c000014, 1 << 12 | 1 << 0, 1 << 12 | 1 << 0)},
	{AOV_REG_INFO(0x1c500000, 0, 0)},
	{AOV_REG_INFO(0x1c500008, 1, 0)},
	{AOV_REG_INFO(0x1c000010, 1 << 29, 1 << 29)},
};
AOV_DECLARE_GLOBAL_OBJ(sadc);

/****** thm *******/
AOV_DECLARE_REG_LIST(thm) = {
	{AOV_REG_INFO(0x1c000370, 0, 1 << 0)},
	{AOV_REG_INFO(0x1c000378, 1 << 5, 1 << 5)},
};
AOV_DECLARE_GLOBAL_OBJ(thm);





/****** gpio *******/
AOV_DECLARE_REG_LIST(gpio) = {
	{AOV_REG_INFO(0x10200000, 0x01, 0)},
	{AOV_REG_INFO(0x10200004, 0x01, 0)},
	{AOV_REG_INFO(0x10200008, 0x01, 0)},
	{AOV_REG_INFO(0x1020000c, 0x01, 0)},

	{AOV_REG_INFO(0x10200010, 0x01, 0)},
	{AOV_REG_INFO(0x10200014, 0x01, 0)},
	{AOV_REG_INFO(0x10200018, 0x01, 0)},
	{AOV_REG_INFO(0x1020001c, 0x01, 0)},

	{AOV_REG_INFO(0x10200020, 0x01, 0)},
	{AOV_REG_INFO(0x10200024, 0x01, 0)},
	{AOV_REG_INFO(0x10200028, 0x01, 0)},
	{AOV_REG_INFO(0x1020002c, 0x01, 0)},

	{AOV_REG_INFO(0x10200030, 0x01, 0)},
	{AOV_REG_INFO(0x10200034, 0x01, 0)},
	{AOV_REG_INFO(0x10200038, 0x01, 0)},
	{AOV_REG_INFO(0x1020003c, 0x01, 0)},

	// {AOV_REG_INFO(0x10200040, 0x01, 0)},
	// {AOV_REG_INFO(0x10200044, 0x01, 0)},
	// {AOV_REG_INFO(0x10200048, 0x01, 0)},
	// {AOV_REG_INFO(0x1020004c, 0x01, 0)},

	{AOV_REG_INFO(0x10200050, 0x01, 0)},
	{AOV_REG_INFO(0x10200054, 0x01, 0)},
	{AOV_REG_INFO(0x10200058, 0x01, 0)},
	{AOV_REG_INFO(0x1020005c, 0x01, 0)},

	{AOV_REG_INFO(0x10200060, 0x01, 0)},
	{AOV_REG_INFO(0x10200064, 0x01, 0)},
	{AOV_REG_INFO(0x10200068, 0x01, 0)},
	{AOV_REG_INFO(0x1020006c, 0x01, 0)},

	{AOV_REG_INFO(0x10200070, 0x01, 0)},
	{AOV_REG_INFO(0x10200074, 0x01, 0)},
	{AOV_REG_INFO(0x10200078, 0x01, 0)},
	{AOV_REG_INFO(0x1020007c, 0x01, 0)},


	{AOV_REG_INFO(0x10200080, 0x01, 0)},
	{AOV_REG_INFO(0x10200084, 0x01, 0)},
	{AOV_REG_INFO(0x10200088, 0x01, 0)},
	{AOV_REG_INFO(0x1020008c, 0x01, 0)},

	{AOV_REG_INFO(0x10200090, 0x01, 0)},
	{AOV_REG_INFO(0x10200094, 0x01, 0)},
	{AOV_REG_INFO(0x10200098, 0x01, 0)},
	{AOV_REG_INFO(0x1020009c, 0x01, 0)},

	{AOV_REG_INFO(0x102000a0, 0x01, 0)},
	{AOV_REG_INFO(0x102000a4, 0x01, 0)},
	{AOV_REG_INFO(0x102000a8, 0x01, 0)},
	{AOV_REG_INFO(0x102000ac, 0x01, 0)},


	{AOV_REG_INFO(0x102000b0, 0x01, 0)},
	{AOV_REG_INFO(0x102000b4, 0x01, 0)},
	{AOV_REG_INFO(0x102000b8, 0x01, 0)},
	{AOV_REG_INFO(0x102000bc, 0x01, 0)},


	{AOV_REG_INFO(0x102000c0, 0x01, 0)},
	{AOV_REG_INFO(0x102000c4, 0x01, 0)},
	{AOV_REG_INFO(0x102000c8, 0x01, 0)},
	{AOV_REG_INFO(0x102000cc, 0x01, 0)},


	{AOV_REG_INFO(0x102000d0, 0x01, 0)},
	{AOV_REG_INFO(0x102000d4, 0x01, 0)},
	{AOV_REG_INFO(0x102000d8, 0x01, 0)},
	{AOV_REG_INFO(0x102000dc, 0x01, 0)},


	{AOV_REG_INFO(0x102000e0, 0x01, 0)},
	{AOV_REG_INFO(0x102000e4, 0x01, 0)},
	{AOV_REG_INFO(0x102000e8, 0x01, 0)},

	//ac ~ b0
	{AOV_REG_INFO(0x102000ac, 0x00, 0)},
	{AOV_REG_INFO(0x102000b0, 0x00, 0)},

	{AOV_REG_INFO(0x0b000008, 0x0, 0)},
	{AOV_REG_INFO(0x0b000028, 0x0, 0)},

	{AOV_REG_INFO(0x0b010008, 0x0, 0)},
	{AOV_REG_INFO(0x0b010028, 0x0, 0)},

	{AOV_REG_INFO(0x0b020008, 0x0, 0)},
	{AOV_REG_INFO(0x0b020028, 0x0, 0)},

	{AOV_REG_INFO(0x0b030008, 0x0, 0)},
	{AOV_REG_INFO(0x0b030028, 0x0, 0)},

	{AOV_REG_INFO(0x0b040008, 0x0, 0)},
	{AOV_REG_INFO(0x0b040028, 0x0, 0)},

	{AOV_REG_INFO(0x0b050008, 0x0, 0)},
	{AOV_REG_INFO(0x0b050028, 0x0, 0)},

	{AOV_REG_INFO(0x0b060008, 0x0, 0)},
	{AOV_REG_INFO(0x0b060028, 0x0, 0)},

	//cpu sys pin
	{AOV_REG_INFO(0x0b100008, 0x0, 7 << 0)},
	{AOV_REG_INFO(0x0b10000c, 0x0, 7 << 0)},
	{AOV_REG_INFO(0x0b100010, 0x0, 7 << 0)},
	{AOV_REG_INFO(0x0b100014, 0x0, 7 << 0)},


	//isp sys pin
	{AOV_REG_INFO(0x2c000010, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000014, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000018, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c00001c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x2c000020, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000024, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000028, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c00002c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x2c000030, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000034, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000038, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c00003c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x2c000040, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000044, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000048, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c00004c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x2c000050, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000054, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000058, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c00005c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x2c000060, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000064, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c000068, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x2c00006c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x2c000070, 0x00, 7 << 0)},

	//veu sys pin
	{AOV_REG_INFO(0x1c100018, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c10001c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x1c100020, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100024, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100028, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c10002c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x1c100030, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100034, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100038, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c10003c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x1c100040, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100044, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100048, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c10004c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x1c100050, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100054, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100058, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c10005c, 0x00, 7 << 0)},

	{AOV_REG_INFO(0x1c100060, 0x00, 7 << 0)},
	{AOV_REG_INFO(0x1c100064, 0x00, 7 << 0)},

};
AOV_DECLARE_GLOBAL_OBJ(gpio);



void lp_plat_suspend(void)
{

	//AOV_PUSH_ALL_REGS(aud);
	AOV_PUSH_ALL_REGS(usb);
	AOV_PUSH_ALL_REGS(ephy);
	//AOV_PUSH_ALL_REGS(sadc);
	//AOV_PUSH_ALL_REGS(thm);
	AOV_PUSH_ALL_REGS(ldo);
	//AOV_PUSH_ALL_REGS(gpio);
}

void lp_plat_resume(void)
{
	//AOV_POP_ALL_REGS(gpio);
	AOV_POP_ALL_REGS(ldo);
	//AOV_POP_ALL_REGS(thm);
	//AOV_POP_ALL_REGS(sadc);
	AOV_POP_ALL_REGS(ephy);
	AOV_POP_ALL_REGS(usb);
	//AOV_POP_ALL_REGS(aud);

}
